<?php
/**
 * [CLOUD]版权所有，保留所有权利
 * This is not a freeware, use is subject to license terms
 *
 * $Id：Mail.php
 */
class Mail
{
    private $link;
    
    /**
     * 分隔符
     * @var string
     */
    private $delimitate;
    
    /**
     * 主机名
     * @var string
     */
    private $host;
    
    /**
     * 端口号
     * @var int
     */
    private $port;
    
    /**
     * 是否要求身份验证
     * @var bool
     */
    private $auth;
    private $from;
    private $mailName;
    private $timeout;
    private $user;
    private $pass;
    private $debug;
    private $sendType;
    
    public function __construct($param = array())
    {
        $this->mail($param);
    }
    
    public function mail($param = array())
    {
        $systemConfig = Common::getConfig( 'System' );
        $this->link = null;
        $this->delimitate = $systemConfig['delimitate'] == 1 ? "\r\n" : ($systemConfig['delimitate'] == 2 ? "\r" : "\n");
        $this->host = $systemConfig['host'];
        $this->port = $systemConfig['port'];
        $this->auth = $systemConfig['auth'];
        $this->from = $systemConfig['from'];
        $this->mailName = $systemConfig['mail_name'];
        $this->timeout = 30;
        $this->user = $systemConfig['user'];
        $this->pass = $systemConfig['pass'];
        $this->sendType = $systemConfig['send_type'];
        $this->debug = 1;
        
        if ($param)
        {
            foreach ($param as $k => $v)
            {
                $this->$k = $v;
            }
        }
        $this->connect($this->host, $this->port, $this->timeout);
    }
    
    public function send_mail( $to, $from, $subject, $message)
    {
        $from = $from ? $from : $this->from;
        $from = "=?" . strtolower( CHARSET ) . "?B?" . base64_encode( $this->mailName ? $this->mailName : SITENAME ) . "?= <" . $from . ">";
        $subject = "=?" . CHARSET . "?B?" . base64_encode( $subject ) . "?=";
        $message = str_replace("\r\n.", " \r\n..", str_replace("\n", "\r\n", str_replace( "\r", "\n", str_replace( "\r\n", "\n", str_replace( "\n\r", "\r", $message ) ) ) ) );
            
        $header = "From: $from{$this->delimitate}X-Priority: 3{$this->delimitate}X-Mailer: bluecms {$this->delimitate}MIME-Version: 1.0{$this->delimitate}Content-type: text/plain; charset=".CHARSET."{$this->delimitate}";
        
        if ( $this->sendType == 0 )
        {
            return false;
        }
        elseif ( $this->sendType == 1)
        {
            if (!$this->connected())
            {
                $this->connect($this->host, 0);
            }
            if ( $this->auth )
            {
                if ($this->c_ehlo())
                {
                    $this->c_auth();
                }
                $from = $this->user;
            }
            else
            {
                $this->c_helo();
                $from = $this->from;
            }
            $this->c_mail( $from );
            foreach ( explode(',', $to) as $k => $v )
            {
                $this->c_rcpt($v);
            }
            $this->c_data();
            $header .= 'Message-ID: <'.gmdate('YmdHs').'.'.substr(md5($message.microtime()), 0, 6).rand(100000, 999999).'@'.$_SERVER['HTTP_HOST'].">{$this->delimitate}";
        	$this->send_data('Date: ' . gmdate('r') . "\r\n");
        	$this->send_data('To: ' . $to . "\r\n");
        	$this->send_data('Subject: ' . $subject . "\r\n");
            $this->send_data($header . "\r\n");
            $this->send_data("\r\n\r\n");
            $this->send_data($message . "\r\n.\r\n");
            return true;
        }
        elseif ( $this->sendType == 2 && function_exists( 'mail' ))
        {
            return @mail($to, $subject, $message, $header);
        }
        else
        { 
            ini_set( 'SMTP' , $this->host );
        	ini_set( 'smtp_port' , $this->port );
        	ini_set( 'sendmail_from' , $from );
        
        	return @mail( $to, $subject, $message, $header );
        }
    }
    
    public function connect( $host, $port = 0, $timeout = 30 )
    {
        if ( $this->connected() )
        {
            return false;
        }
        else
        {
            if ( empty( $port ) )
            {
                $port = $this->port;
            }
            $this->link = fsockopen( $host , $port , $errno , $errstr , $timeout );
            if ( !$this->link )
            {
                $this->error = array('error'  =>'Failed to connect to server',
                                     'errno'  =>$errno,
                                     'errstr' =>$errstr);
                if ($this->debug >= 1)
                {
                    exit('Error: ' . $this->error['error']);
                }
                return false;
            }
            
            stream_set_blocking($this->link, true);
            /*if (substr(PHP_OS, 0, 3) != 'WIN')
            {
                stream_set_timeout($this->link, $timeout, 0);
            }*/
            $reply = $this->get_status();
            if (substr($reply, 0, 3) != '220')
            {
                $this->error = array('error' => 'Failed to create link',
                					 'msg'   => substr($reply, 4),
                                     'code'  => substr($reply, 0, 3));
                if ($this->debug >= 1)
                {
                    exit('Error:' . $this->error['error']);
                }
            }
        }
    }
    
    private function c_auth()
    {
        if ( $this->auth )
        {
            $flag = 0;
            $this->send_data("AUTH LOGIN\r\n");
            $reply = $this->get_status();
            if (substr($reply, 0, 3) == '334')
            {
                $this->send_data(base64_encode($this->user)."\r\n");
                $reply = $this->get_status();
                if (substr($reply, 0, 3) == '334')
                {
                    $this->send_data(base64_encode($this->pass)."\r\n");
                    $reply = $this->get_status();
                    if (substr($reply, 0, 3) == '235')
                    {
                        return true;
                    }
                    else
                    {
                        $flag = 1;
                    }
                }
                else
                {
                    $flag = 1;
                }
            }
            else 
            {
                $flag = 1;
            }
            if ($flag == 1)
            {
                $this->error = array('error' => 'AUTH command failed',
                                     'msg'   => substr($reply, 4),
                                     'code'  => substr($reply, 0 ,3));
                if ($this->debug >= 1)
                {
                    exit('Error:' . $this->error['error']);
                }
                return false;
            }
        }
    }
    
    private function c_helo()
    {
        $this->send_data("HELO bluecms\r\n");
        $reply = $this->get_status();
        if (substr($reply, 0, 3) == '250' || substr($reply, 0, 3) == '220')
        {
            return true;
        }
        else
        {
            $this->error = array('error' => 'HELO command failed',
                                 'msg'   => substr($reply, 4),
                                 'code'  => substr($reply, 0 ,3));
            if ($this->debug >= 1)
            {
                exit('Error:' . $this->error['error']);
            }
            return false;
        }
    }
    
    private function c_ehlo()
    {
        $this->send_data("EHLO bluecms\r\n");
        $reply = $this->get_status();
        if (substr($reply, 0, 3) == '250' || substr($reply, 0, 3) == '220')
        {
            return true;
        }
        else
        {
            $this->error = array('error' => 'EHLO command failed',
                                 'msg'   => substr($reply, 4),
                                 'code'  => substr($reply, 0 ,3));
            if ($this->debug >= 1)
            {
                exit('Error:' . $this->error['error']);
            }
            return false;
        }
    }
    
    private function c_mail($from)
    {
        $this->send_data("MAIL FROM:<" . preg_replace("/.*\<(.+?)\>.*/", "\\1", $from) . ">\r\n");
        $reply = $this->get_status();
        if (substr($reply, 0, 3) == '250')
        {
            return true;
        }
        else
        {
            $this->send_data("MAIL FROM:<" . preg_replace("/.*\<(.+?)\>.*/", "\\1", $from) . ">\r\n");
            if (substr($reply, 0, 3) == '250')
            {
                return true;
            }
            else
            {
                $this->error = array('error' => 'MAIL command failed',
                                     'msg'   => substr($reply, 4),
                                     'code'  => substr($reply, 0 ,3));
                if ($this->debug >= 1)
                {
                    exit('Error:' . $this->error['error']);
                }
                return false;
            }
        }
    }
    
    private function c_rcpt($to)
    {
        $this->send_data("RCPT TO:<" . preg_replace("/.*\<(.+?)\>.*/", "\\1", $to) . ">\r\n");
        $reply = $this->get_status();
        if (substr($reply, 0, 3) == '250')
        {
            return true;
        }
        else
        {
            $this->error = array('error' => 'RCPT command failed',
                                 'msg'   => substr($reply, 4),
                                 'code'  => substr($reply, 0 ,3));
            if ($this->debug >= 1)
            {
                exit('Error:' . $this->error['error']);
            }
            return false;
        }
    }
    
    private function c_data()
    {
        $this->send_data("DATA\r\n");
        $reply = $this->get_status();
        if (substr($reply, 0, 3) == '354')
        {
            return true;
        }
        else
        {
            $this->error = array('error' => 'DATA command failed',
                                 'msg'   => substr($reply, 4),
                                 'code'  => substr($reply, 0 ,3));
            if ($this->debug >= 1)
            {
                exit('Error:' . $this->error['error']);
            }
            return false;
        }
    }
    
    private function c_quit()
    {
        $this->send_data("QUIT\r\n");
        $reply = $this->get_status();
        echo $reply;exit;
        if (substr($reply, 0, 3) == '221')
        {
            return true;
        }
        else
        {
            $this->error = array('error' => 'QUIT command failed',
                                 'msg'   => substr($reply, 4),
                                 'code'  => substr($reply, 0 ,3));
            if ($this->debug >= 1)
            {
                exit('Error:' . $this->error['error']);
            }
            return false;
        }
    }
    
    function send_data($data)
    {
        return fwrite($this->link, $data);
    }
    
    
    private function get_status()
    {
        $data = "";
        while($str = @fgets($this->link,512)) {
          $data .= $str;
          # if the 4th character is a space then we are done reading
          # so just break the loop
          if(substr($str,3,1) == " ") { break; }
        }
        return $data;
    }
    
    private function connected()
    {
        if (!empty($this->link))
        {
            $status = stream_get_meta_data($this->link);
            if ($status['eof'])
            {
                $this->close();
                return false;
            }
            else
            {
                return true;
            }
        }
        return false;
    }
    
    private function close()
    {
        if (!empty($this->link))
        {
            @fclose($this->link);
            $this->link = null;
        }
    }
}